Fetching on Event
If you've ever built a developer portfolio for yourself, odds are you've implemented a contact form. These forms are all over the internet.
Let's see how we can implement one:
Video Summary
- This video shows a contact form implementation. It includes two controlled inputs for email and message, but nothing is wired up yet in terms of submitting the form.
- The strategy is to pass a function to the
onSubmit
attribute on the form, and it will stop the default behavior withevent.preventDefault()
. We'll usefetch
to manage the submission ourselves. - To submit the request, we'll call
fetch
and use the supplied endpoint. Becausefetch
is promise-based, we'll make it an async function, so that we canawait
the response - We want to supply two options:
- Changing the
method
toPOST
, since that's what the endpoint expects. - Supplying the data, the email and message
- We need to stringify the data with
JSON.stringify()
, because it's impossible to send objects over the network. If we don't do this, the browser will try to stringify it for us, and it'll send the string"[object Object]"
. - We'll derive the JSON from the response with
await response.json()
. This is needed because not all responses include the full body right away (eg. if it's a streaming response). - We see that the response was successful, and it reflects the data we submitted for verification.
- This is the fundamental strategy for doing event-based network requests, but there's still much to do in terms of UX! We'll cover that below.
In this video, we see how to send data to our backend API using fetch
. We saw how to send a POST request, how to stringify the body, and how to validate that we received the correct response from the server.
But really, this implementation is not yet complete. We need to update the UI so that the user knows what's happening at all times!
Here's the sandbox from the video above:
Code Playground
Loading, success, and error statuses
When submitting network requests, we want to update the UI to indicate 3 different statuses:
- Loading
- Success
- Error
In the video below, I'll show you how I'd implement these statuses, and update the UI accordingly, but I'd encourage you to give it a shot yourself, using the playground above. Feel free to structure things however you wish, updating the UI in whichever way you feel makes the most sense.
Here's how I'd approach it:
Video Summary
- I create a
status
state variable with 4 possible values:idle
loading
success
error
- When the form is submitted, I immediately change the status to
loading
- How can we tell if the request succeeds? While it's possible to use status codes, this can be unreliable, so we'll check the JSON output. If
json.ok
is truthy, we'll change the status tosuccess
. Otherwise, it'll beerror
. - When the status is
loading
, I'll change the UI in two ways:- All inputs and buttons will be disabled. This is a visual cue that things are happening, while also stopping accidental multi-submissions.
- I'll change the button text from "Submit" to "Submitting…". A spinner would also work well.
- When the status is
success
, we have several options. We could replace the form with a success message, or we could show a small note below the form. We'll want to reset the message, if we choose to continue showing the form. - For the
error
status, we'll show a generic error message below the form.- It's possible to get very granular with errors, but I prefer to rely on HTML validation. We get a lot for free!
- Server-side validation is still important, since crafty users can disable client-side validation, but if someone explicitly disables the user-friendly validation, I see no reason to provide high-quality validation from the server.
In the video above, we touch on HTML validation. You can learn more on MDN: “Constraint Validation”.
We also touched on HTTP status codes. You can learn more in the “HTTP Status Codes” primer lesson 👀.
Here's the final sandbox from the video:
Code Playground